
jQuery.extend(fn, {
	// Builds the initial folder list with data in the page
	initFolders: function() {
		var qtfile = this, options = qtfile.options, rootFolderName,
			// Root ul element of folder list
    		$folderList = qtfile.find(options.folderList + ' > ul'),
			// Folder path -> folder <li>
			folderPaths = qtfile.folderPaths = [];

		// Folder list not exists
		if ($folderList.size() == 0) {
			$folderList = jQuery(HtmlTagUl).appendTo(qtfile.find(options.folderList));
		}

		qtfile.folderList = $folderList;

		rootFolderName = jQuery.trim($folderList.attr(AttrTitle) || options.rootFolder);

		$folderList.removeAttr(AttrTitle).addClass(options.classes.folderList);

		// Save all folder paths
		$folderList.children(Li).each(function() {
			var $this = jQuery(this),
				path = parseJSON($this.text()).path;
			folderPaths[path] = qtfile.createFolderItem(path)
				// Copy css, e.g. class of selected folder
				.addClass($this.attr('class'));
			$this.remove();
		});

		// Empty to indicate root folder of current user
		qtfile.rootFolder = qtfile.createFolderItem('', rootFolderName).appendTo($folderList);

		qtfile.resetFolders();
	},
	// Creates a li element for a folder
	createFolderItem: function(fullPath, name) {
		var qtfile = this, classes = qtfile.options.classes,
			folderName = name || qtfile.splitPath(fullPath).name,

			$folderItem = jQuery(HtmlTagLi)
				.attr(AttrTitle, folderName)
				.data(DataKeyFolderPath, fullPath);

		// Folder bullet
		jQuery(HtmlTagSpan)
			.addClass(classes.folderBullet)
			.appendTo($folderItem);

		// Folder name
		jQuery(HtmlTagSpan)
			.addClass(classes.folderName)
			.click(function() {
				qtfile.selectFolder(jQuery(this).parent(Li));
			})
			// Folder icon
			.append(jQuery(HtmlTagSpan).addClass(classes.folderIcon))
			// Folder name text
			.append(jQuery(HtmlTagSpan).addClass(classes.folderNameText).text(folderName))
			// Tail icon
			.append(jQuery(HtmlTagSpan).addClass(classes.folderTailIcon))
			.appendTo($folderItem);

		return $folderItem;
	}, // End of createFolderItem()
	// Handler for a folder is being selected
	selectFolder: function($folderItem) {
		var qtfile = this;
		if (qtfile.folderMovingPath)
			qtfile.moveFolderEnd($folderItem);
		else if (qtfile.fileMovingPath)
			qtfile.moveFileEnd($folderItem);
		else
			qtfile.changeFolder($folderItem);
	}, // End of onFolderNameClicked()
	resetFolders: function() {
		var qtfile = this,
			$rootFolder = qtfile.rootFolder,
			$currentFolder = qtfile.currentFolder,
			$folderList = qtfile.folderList;

		$rootFolder.children(Ul).remove();

		for (var path in qtfile.folderPaths) {
			qtfile.addFolder(path);
		}

		// Set root folder as current folder if none is current selected
		$currentFolder = $folderList.find(
			'.' + qtfile.options.classes.folderSelected);
		if ($currentFolder.size() == 0) {
			$currentFolder = $rootFolder;
		}

		qtfile.currentFolder = $currentFolder;
		qtfile.updateFolder($currentFolder);
	},
	addFolder: function(path) {
		var qtfile = this,
			$this = qtfile.folderPaths[path],

			// Extract folder path
			folder = qtfile.splitPath(path),
			folderName = folder.name,
			parentPath = folder.parent;

		qtfile.appendFolder($this, parentPath ?
			qtfile.folderPaths[parentPath] :
			qtfile.rootFolder);
	},
	appendFolder: function($folderItem, $parent) {
		if ($parent.children(Ul).size() == 0) {
			$parent.append(HtmlTagUl);
		}
		$folderItem.appendTo($parent.children(Ul));

		this.updateFolderIcon($parent);
	}, // End of appendFolder()
	updateFolderIcon: function($folder) {
		var classes = this.options.classes;
		if ($folder.children(Ul).children(Li).size()) {

			// add close/open folder list event
			$folder.children('.' + classes.folderBullet)
				.addClass(classes.folderBulletOpened)
				.unbind('click')
				.click(function() {
					jQuery(this)
						.toggleClass(classes.folderBulletOpened)
						.toggleClass(classes.folderBulletClosed)
						.siblings(Ul)
						// Open/Close subfolders
						.slideToggle('slow');
				});

		} else {
			$folder.children('.' + classes.folderBullet)
				.removeClass(classes.folderBulletOpened)
				.removeClass(classes.folderBulletClosed)
				.unbind('click');
			$folder.children(Ul).remove();
		}
	},
	// Updates current to a new folder
	updateFolder: function($newCurrentFolder) {
		var qtfile = this,
			$currentFolder = qtfile.currentFolder,
			classes = qtfile.options.classes;

		$currentFolder
			.removeClass(classes.folderSelected)
			.children('.' + classes.folderName)
			.removeClass(classes.folderNameSelected);

		qtfile.currentFolder = 
			$currentFolder = 
			$newCurrentFolder.addClass(classes.folderSelected);
			
		$currentFolder
			.children('.' + classes.folderName)
			.addClass(classes.folderNameSelected);
	}, // End of updateCurrentFolder()
	// Changes current folder and loads the file list
	changeFolder: function($newFolder, enforce) {
		var qtfile = this, options = qtfile.options;

		// Enforce reload or The folder is not currently selected
		if (enforce || !$newFolder.hasClass(options.classes.folderSelected)) {

			if (qtfile.tryLock(TextFileListInProgress, TextFileListWait)) {

				// Send request to server
				qtfile.request(
					options.file.list,
					{
						path: $newFolder.data(DataKeyFolderPath)
					},
					function(data) {

						// Make sure a file list is returned
						if (jQuery.isArray(data)) {
							var files = [];
							jQuery.each(data, function() {
								files.push(this);
							});

							// update current folder
							qtfile.updateFolder($newFolder);

							qtfile.resetFiles(files);

							qtfile.unlock(TextFileListSucceed);
						} else {
							qtfile.unlock(translateFileListStatus(data.status), qtfile.statusClass(data));
						}
					},
					function() {
						qtfile.unlock(TextFileListError, options.classes.statusError);
					},
					function() {
						qtfile.updateButtons();
					}
				);
			} // End if
		} // End if
	}, // End of changeCurrentFolder()
	// Creates a new folder
	createFolder: function($parentFolder) {
		var qtfile = this, options = qtfile.options, folderName;

		if (qtfile.tryLock(TextFolderCreateInProgress, TextFolderCreateWait)) {

			// Ask for folder name
			folderName = prompt(TextFolderCreateQuestion, TextFolderCreateNewFolder);

			if (folderName) {
				if (options.folderName.test(folderName)) {

					isLoading = true;

					var newFolderPath = qtfile.combinePath($parentFolder.data(DataKeyFolderPath), folderName);

					// Send request to server
					qtfile.request(
						options.folder.create,
						{
							path: newFolderPath
						},
						function(data) {

							if (data.succeed) {

								// Append new folder to folder list
								qtfile.folderPaths[newFolderPath] = qtfile.createFolderItem(newFolderPath);
								qtfile.addFolder(newFolderPath);
							}
							qtfile.unlock(translateFolderCreateStatus(data.status), qtfile.statusClass(data));
						},
						function() {
							qtfile.unlock(TextFolderCreateError, options.classes.statusError);
						}
					);

				} else {
					alert(TextFolderInvalidFolderName);
					qtfile.unlock();
				}
			} else {
				qtfile.unlock();
			}
		}
	}, // End of createFolder
	// Renames a fodler
	renameFolder: function($folderItem) {
		var qtfile = this, $currentFolder = qtfile.currentFolder,
			options = qtfile.options,
			path = $currentFolder.data(DataKeyFolderPath);

		// Not root folder
		if (path.length) {

			if (qtfile.tryLock(TextFolderRenameInProgress, TextFolderRenameWait)) {

				var currentName = qtfile.splitPath(path),
					folderName = prompt(TextFolderRenameQuestion, currentName.name);

				if (!folderName) {
					qtfile.unlock();
					return;
				}

				// Test folder name
				if (!options.folderName.test(folderName)) {
					alert(TextFolderInvalidFolderName);
					qtfile.unlock();
					return;
				}

				var destPath = qtfile.combinePath(currentName.parent, folderName);

				// Send request to server
				qtfile.request(
					options.folder.move,
					{
						srcPath: path,
						destPath: destPath
					},
					function(data) {
						if (data.succeed) {

							qtfile.updateFolderPath($currentFolder, destPath);

							// update folder to new name
							$currentFolder
								.children('.' + options.classes.folderName)
								.children('.' + options.classes.folderNameText)
								.text(folderName);
						}
						
						qtfile.unlock(translateFolderRenameStatus(data.status), qtfile.statusClass(data));
					},
					function() {
						qtfile.unlock(TextFolderRenameError, options.classes.statusError);
					}
				); // End of request
			} // End if
		} // End if
	}, // End of renameFolder
	// Deletes the current folder
	deleteFolder: function($folderItem) {
		var qtfile = this, $currentFolder = qtfile.currentFolder,
			options = qtfile.options,
			path = $currentFolder.data(DataKeyFolderPath);

		// not root folder
		if (path.length) {

			if (qtfile.tryLock(TextFolderDeleteInProgress, TextFolderDeleteWait)) {

				if (!confirm(TextFolderDeleteQuestion)) {
					qtfile.unlock();
					return;
				}

				// Send request to server
				qtfile.request(
					options.folder.del,
					{
						path: path
					},
					function(data) {
						if (data.succeed) {

							var $parent = $currentFolder.parent(Ul).parent(Li);
							$currentFolder.remove();
							qtfile.updateFolderIcon($parent);

							qtfile.unlock(TextFolderDeleteSucceed);

							// Move to root folder, since current has been deleted
							qtfile.changeFolder(qtfile.rootFolder);
						} else {
							qtfile.unlock(translateFolderDeleteStatus(data.status), qtfile.statusClass(data));
						}

					},
					function() {
						qtfile.unlock(TextFolderDeleteError, options.classes.statusError);
					}
				);

			} // End if
		} // End if
	}, // End of deleteFolder
	// Reload folder list
	reloadFolders: function() {
		var qtfile = this,
			options = qtfile.options;
		if (qtfile.tryLock(TextFolderListInProgress, TextFolderListWait)) {

			// Send request to server
			qtfile.request(
				options.folder.list,
				{},
				function(data) {
					
					if (jQuery.isArray(data)) {
						// Reset folder path
						qtfile.folderPaths = [];
						
						jQuery.each(data, function() {
							qtfile.folderPaths[this.path] = qtfile.createFolderItem(this.path);
						});
						
						// Get original folder item before refresh folder list
						var orgPath = qtfile.currentFolder.data(DataKeyFolderPath),
							$newCurrentFolder = orgPath.length > 0 ?
							qtfile.folderPaths[qtfile.currentFolder.data(DataKeyFolderPath)] :
							qtfile.rootFolder;
						
						qtfile.resetFolders();
						qtfile.unlock(TextFolderListSucceed);
						
						if ($newCurrentFolder) {
							// Update current folder since it has been rebuild
							qtfile.updateFolder($newCurrentFolder);
						}
						else {
							// Original current folder has been removed,
							// move to root folder
							qtfile.changeFolder(qtfile.rootFolder, true);
						}
					} else {
						qtfile.unlock(data, qtfile.statusClass(data));
					}
				},
				function() {
					qtfile.unlock(TextFolderListError, options.classes.statusError);
				}
			); // End request
		}
	},
	// Updates the path info of a folder and its children
	updateFolderPath: function($folder, newPath) {
		var folderPaths = this.folderPaths,

			oldPath = $folder.data(DataKeyFolderPath);
		$folder.find(Li).andSelf().each(function() {
			$this = jQuery(this);

			// Make new path
			var oldSubfolderPath = $this.data(DataKeyFolderPath);
			var newSubfolderPath = oldSubfolderPath.replace(oldPath, newPath);

			// Update pate data
			$this.data(DataKeyFolderPath, newSubfolderPath);

			folderPaths[newSubfolderPath] = $this;
			delete folderPaths[oldSubfolderPath];

		});
	}, // End of updateFolderPath()
	// Marks a file as the file being moved
	moveFolderBegin: function($folderItem) {
		var qtfile = this;
		if (qtfile.tryLock(TextFolderMoveInProgress, TextFolderMoveWait)) {
			qtfile.folderMovingPath = $folderItem.data(DataKeyFolderPath);
		}
	},
	// Moves previous selected file to destination folder
	moveFolderEnd: function($newParent) {
		var qtfile = this, folderMovingPath = qtfile.folderMovingPath,
			options = qtfile.options, folderPaths = qtfile.folderPaths,
			sp = options.folderSeparator;
		if (!folderMovingPath)
			return;

		var newParentPath = $newParent.data(DataKeyFolderPath);

		// parent, current of subfolder selected
		if (folderMovingPath == newParentPath ||
			qtfile.splitPath(folderMovingPath).parent == newParentPath ||
			(sp + newParentPath + sp).indexOf(sp + folderMovingPath) == 0) {

			qtfile.folderMovingPath = null;

			var newLen = newParentPath.length,
				oldLen = folderMovingPath.length;

			if (newLen > oldLen) // Subfolder selected
				qtfile.unlock(TextFolderMoveSubfolderSelected,
					options.classes.statusError);
			else if (newLen < oldLen) // Parent folder selected
				qtfile.unlock(TextFolderMoveAlreadyExist,
					options.classes.statusError);
			else // Current folder selected
				qtfile.unlock(TextFolderMoveCurrentSelected,
					options.classes.statusError);
			return;
		}

		var orgName = qtfile.splitPath(folderMovingPath),
			destPath = qtfile.combinePath(newParentPath, orgName.name);

		// Send request to server
		qtfile.request(
			options.folder.move,
			{
				srcPath: folderMovingPath,
				destPath: destPath
			},
			function(data) {
				if (data.succeed) {

					qtfile.updateFolderPath(qtfile.currentFolder, destPath);

					qtfile.appendFolder(folderPaths[destPath], $newParent);

					// Update old parent since it maybe empty
					qtfile.updateFolderIcon(orgName.parent ? folderPaths[orgName.parent] : qtfile.rootFolder);
				}
				qtfile.unlock(translateFolderMoveStatus(data.status), qtfile.statusClass(data));

			},
			function() {
				qtfile.unlock(TextFolderMoveError, options.classes.statusError);
			},
			function() {
				qtfile.folderMovingPath = null;
			}); // End request
	}
});																											
